home *** CD-ROM | disk | FTP | other *** search
/ STraTOS 1997 April & May / STraTOS 1 - 1997 April & May.iso / CD01 / INTERNET / SITES / LITTLE / P3SRC.ZIP / ATARI / NORMAL.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-07  |  16.9 KB  |  820 lines

  1. /****************************************************************************
  2. *                normal.c
  3. *
  4. *  This module implements solid texturing functions that perturb the surface
  5. *  normal to create a bumpy effect. 
  6. *
  7. *  from Persistence of Vision(tm) Ray Tracer
  8. *  Copyright 1996 Persistence of Vision Team
  9. *---------------------------------------------------------------------------
  10. *  NOTICE: This source code file is provided so that users may experiment
  11. *  with enhancements to POV-Ray and to port the software to platforms other 
  12. *  than those supported by the POV-Ray Team.  There are strict rules under
  13. *  which you are permitted to use this file.  The rules are in the file
  14. *  named POVLEGAL.DOC which should be distributed with this file. If 
  15. *  POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  16. *  Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  17. *  Forum.  The latest version of POV-Ray may be found there as well.
  18. *
  19. * This program is based on the popular DKB raytracer version 2.12.
  20. * DKBTrace was originally written by David K. Buck.
  21. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  22. *
  23. *****************************************************************************/
  24.  
  25. /*
  26.  * Some texture ideas garnered from SIGGRAPH '85 Volume 19 Number 3,
  27.  * "An Image Synthesizer" By Ken Perlin.
  28.  *
  29.  * Further Ideas Garnered from "The RenderMan Companion" (Addison Wesley)
  30.  */
  31.  
  32. #include "frame.h"
  33. #include "vector.h"
  34. #include "povproto.h"
  35. #include "texture.h"
  36. #include "image.h"
  37. #include "matrices.h"
  38. #include "normal.h"
  39. #include "povray.h"
  40. #include "txttest.h"
  41. #include "pigment.h"
  42.  
  43.  
  44.  
  45. /*****************************************************************************
  46. * Local preprocessor defines
  47. ******************************************************************************/
  48.  
  49.  
  50.  
  51. /*****************************************************************************
  52. * Local typedefs
  53. ******************************************************************************/
  54.  
  55.  
  56.  
  57. /*****************************************************************************
  58. * Local constants
  59. ******************************************************************************/
  60.  
  61. static CONST
  62. VECTOR Pyramid_Vect [4]= {{ 0.942809041,-0.333333333, 0.0},
  63.                           {-0.471404521,-0.333333333, 0.816496581},
  64.                           {-0.471404521,-0.333333333,-0.816496581},
  65.                           { 0.0        , 1.0        , 0.0}};
  66.  
  67.  
  68. /*****************************************************************************
  69. * Static functions
  70. ******************************************************************************/
  71.  
  72. static void ripples PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR Vector));
  73. static void waves PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR Vector));
  74. static void bumps PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR normal));
  75. static void dents PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR normal));
  76. static void wrinkles PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR normal));
  77. static void quilted PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR normal));
  78. static DBL Hermite_Cubic PARAMS((DBL T1,UV_VECT UV1,UV_VECT UV2));
  79. static DBL Do_Slope_Map PARAMS((DBL value, BLEND_MAP *Blend_Map));
  80. static void Do_Average_Normals PARAMS((VECTOR EPoint, TNORMAL *Tnormal, VECTOR normal));
  81.  
  82.  
  83. /*****************************************************************************
  84. *
  85. * FUNCTION
  86. *
  87. *   ripples
  88. *
  89. * INPUT
  90. *   
  91. * OUTPUT
  92. *   
  93. * RETURNS
  94. *   
  95. * AUTHOR
  96. *
  97. *   POV-Ray Team
  98. *   
  99. * DESCRIPTION
  100. *
  101. * CHANGES
  102. *
  103. ******************************************************************************/
  104.  
  105. static void ripples (EPoint, Tnormal, normal)
  106. VECTOR EPoint;
  107. TNORMAL *Tnormal;
  108. VECTOR normal;
  109. {
  110.   register unsigned int i;
  111.   register DBL length, scalar, index;
  112.   VECTOR point;
  113.  
  114.   for (i = 0 ; i < Number_Of_Waves ; i++)
  115.   {
  116.     VSub (point, EPoint, Wave_Sources[i]);
  117.     VLength (length, point);
  118.  
  119.     if (length == 0.0)
  120.       length = 1.0;
  121.  
  122.     index = length * Tnormal->Frequency + Tnormal->Phase;
  123.  
  124.     scalar = cycloidal(index) * Tnormal ->Amount;
  125.  
  126.     VAddScaledEq(normal, scalar / (length * (DBL)Number_Of_Waves), point);
  127.   }
  128. }
  129.  
  130.  
  131.  
  132. /*****************************************************************************
  133. *
  134. * FUNCTION
  135. *
  136. *   waves
  137. *
  138. * INPUT
  139. *   
  140. * OUTPUT
  141. *   
  142. * RETURNS
  143. *   
  144. * AUTHOR
  145. *
  146. *   POV-Ray Team
  147. *   
  148. * DESCRIPTION
  149. *
  150. * CHANGES
  151. *
  152. ******************************************************************************/
  153.  
  154. static void waves (EPoint, Tnormal, normal)
  155. VECTOR EPoint;
  156. TNORMAL *Tnormal;
  157. VECTOR normal;
  158. {
  159.   register unsigned int i;
  160.   register DBL length, scalar, index, sinValue ;
  161.   VECTOR point;
  162.  
  163.   for (i = 0 ; i < Number_Of_Waves ; i++)
  164.   {
  165.     VSub (point, EPoint, Wave_Sources[i]);
  166.  
  167.     VLength (length, point);
  168.  
  169.     if (length == 0.0)
  170.     {
  171.       length = 1.0;
  172.     }
  173.  
  174.     index = length * Tnormal->Frequency * frequency[i] + Tnormal->Phase;
  175.  
  176.     sinValue = cycloidal(index);
  177.  
  178.     scalar = sinValue * Tnormal->Amount / frequency[i];
  179.  
  180.     VAddScaledEq(normal, scalar / (length * (DBL)Number_Of_Waves), point);
  181.   }
  182. }
  183.  
  184.  
  185.  
  186. /*****************************************************************************
  187. *
  188. * FUNCTION
  189. *
  190. *   bumps
  191. *
  192. * INPUT
  193. *   
  194. * OUTPUT
  195. *   
  196. * RETURNS
  197. *   
  198. * AUTHOR
  199. *
  200. *   POV-Ray Team
  201. *   
  202. * DESCRIPTION
  203. *
  204. * CHANGES
  205. *
  206. ******************************************************************************/
  207.  
  208. static void bumps (EPoint, Tnormal, normal)
  209. VECTOR EPoint;
  210. TNORMAL *Tnormal;
  211. VECTOR normal;
  212. {
  213.   VECTOR bump_turb;
  214.  
  215.   /* Get normal displacement value. */
  216.  
  217.   DNoise (bump_turb, EPoint);
  218.  
  219.   /* Displace "normal". */
  220.  
  221.   VAddScaledEq(normal, Tnormal->Amount, bump_turb);
  222. }
  223.  
  224.  
  225.  
  226. /*****************************************************************************
  227. *
  228. * FUNCTION
  229. *
  230. * INPUT
  231. *   
  232. * OUTPUT
  233. *   
  234. * RETURNS
  235. *   
  236. * AUTHOR
  237. *
  238. *   POV-Ray Team
  239. *
  240. * DESCRIPTION
  241. *   Dents is similar to bumps, but uses noise() to control the amount of
  242. *   dnoise() perturbation of the object normal...
  243. *
  244. * CHANGES
  245. *
  246. ******************************************************************************/
  247.  
  248. static void dents (EPoint, Tnormal, normal)
  249. VECTOR EPoint;
  250. TNORMAL *Tnormal;
  251. VECTOR normal;
  252. {
  253.   DBL noise;
  254.   VECTOR stucco_turb;
  255.  
  256.   noise = Noise (EPoint);
  257.  
  258.   noise = noise * noise * noise * Tnormal->Amount;
  259.  
  260.   /* Get normal displacement value. */
  261.  
  262.   DNoise(stucco_turb, EPoint);
  263.  
  264.   /* Displace "normal". */
  265.  
  266.   VAddScaledEq(normal, noise, stucco_turb);
  267. }
  268.  
  269.  
  270.  
  271.  
  272. /*****************************************************************************
  273. *
  274. * FUNCTION
  275. *
  276. * INPUT
  277. *   
  278. * OUTPUT
  279. *   
  280. * RETURNS
  281. *   
  282. * AUTHOR
  283. *
  284. *   POV-Ray Team
  285. *   
  286. * DESCRIPTION
  287. *
  288. *   Wrinkles - This is my implementation of the dented() routine, using
  289. *   a surface iterative fractal derived from DTurbulence.
  290. *
  291. *   This is a 3-D version (thanks to DNoise()...) of the usual version
  292. *   using the singular Noise()...
  293. *
  294. *   Seems to look a lot like wrinkles, however... (hmmm)
  295. *
  296. *   Idea garnered from the April 89 Byte Graphics Supplement on RenderMan,
  297. *   refined from "The RenderMan Companion, by Steve Upstill of Pixar,
  298. *   (C) 1990 Addison-Wesley.
  299. *
  300. * CHANGES
  301. *
  302. ******************************************************************************/
  303.  
  304. static void wrinkles (EPoint, Tnormal, normal)
  305. VECTOR EPoint;
  306. TNORMAL *Tnormal;
  307. VECTOR normal;
  308. {
  309.   register int i;
  310.   register DBL scale = 1.0;
  311.   VECTOR result, value, value2;
  312.  
  313.   Make_Vector(result, 0.0, 0.0, 0.0);
  314.  
  315.   for (i = 0; i < 10; scale *= 2.0, i++)
  316.   {
  317.     VScale(value2,EPoint,scale);
  318.     DNoise(value, value2);
  319.  
  320.     result[X] += fabs(value[X] / scale);
  321.     result[Y] += fabs(value[Y] / scale);
  322.     result[Z] += fabs(value[Z] / scale);
  323.   }
  324.  
  325.   /* Displace "normal". */
  326.  
  327.   VAddScaledEq(normal, Tnormal->Amount, result);
  328. }
  329.  
  330.  
  331. /*****************************************************************************
  332. *
  333. * FUNCTION
  334. *
  335. *   quilted
  336. *
  337. * INPUT
  338. *   
  339. * OUTPUT
  340. *   
  341. * RETURNS
  342. *   
  343. * AUTHOR
  344. *
  345. *   Dan Farmer '94
  346. *   
  347. * DESCRIPTION
  348. *
  349. * CHANGES
  350. *
  351. ******************************************************************************/
  352.  
  353. static void quilted (EPoint, Tnormal, normal)
  354. VECTOR EPoint;
  355. TNORMAL *Tnormal;
  356. VECTOR normal;
  357. {
  358.   VECTOR value;
  359.   DBL t;
  360.  
  361.   value[X] = EPoint[X]-FLOOR(EPoint[X])-0.5;
  362.   value[Y] = EPoint[Y]-FLOOR(EPoint[Y])-0.5;
  363.   value[Z] = EPoint[Z]-FLOOR(EPoint[Z])-0.5;
  364.  
  365.   t = sqrt(value[X]*value[X]+value[Y]*value[Y]+value[Z]*value[Z]);
  366.  
  367.   t = quilt_cubic(t, Tnormal->Vals.Quilted.Control0, Tnormal->Vals.Quilted.Control1);
  368.  
  369.   value[X] *= t;
  370.   value[Y] *= t;
  371.   value[Z] *= t;
  372.  
  373.   VAddScaledEq (normal, Tnormal->Amount,value);
  374. }
  375.  
  376. /*****************************************************************************
  377. *
  378. * FUNCTION
  379. *
  380. *   Create_Tnormal
  381. *
  382. * INPUT
  383. *   
  384. * OUTPUT
  385. *   
  386. * RETURNS
  387. *
  388. *   pointer to the created Tnormal
  389. *   
  390. * AUTHOR
  391. *
  392. *   POV-Ray Team
  393. *   
  394. * DESCRIPTION   : Allocate memory for new Tnormal and initialize it to
  395. *                 system default values.
  396. *
  397. * CHANGES
  398. *
  399. ******************************************************************************/
  400.  
  401.  
  402. TNORMAL *Create_Tnormal ()
  403. {
  404.   TNORMAL *New;
  405.  
  406.   New = (TNORMAL *)POV_MALLOC(sizeof(TNORMAL), "normal");
  407.  
  408.   Init_TPat_Fields((TPATTERN *)New);
  409.  
  410.   New->Amount = 0.5;
  411.  
  412.   return (New);
  413. }
  414.  
  415.  
  416.  
  417. /*****************************************************************************
  418. *
  419. * FUNCTION
  420. *
  421. *   Copy_Tnormal
  422. *
  423. * INPUT
  424. *   
  425. * OUTPUT
  426. *   
  427. * RETURNS
  428. *   
  429. * AUTHOR
  430. *
  431. *   POV-Ray Team
  432. *   
  433. * DESCRIPTION
  434. *
  435. * CHANGES
  436. *
  437. ******************************************************************************/
  438.  
  439. TNORMAL *Copy_Tnormal (Old)
  440. TNORMAL *Old;
  441. {
  442.   TNORMAL *New;
  443.  
  444.   if (Old != NULL)
  445.   {
  446.     New = Create_Tnormal();
  447.  
  448.     Copy_TPat_Fields ((TPATTERN *)New, (TPATTERN *)Old);
  449.  
  450.     New->Amount = Old->Amount;
  451.   }
  452.   else
  453.   {
  454.     New = NULL;
  455.   }
  456.  
  457.   return (New);
  458. }
  459.  
  460.  
  461.  
  462. /*****************************************************************************
  463. *
  464. * FUNCTION
  465. *
  466. *   Destroy_Tnormal
  467. *
  468. * INPUT
  469. *   
  470. * OUTPUT
  471. *   
  472. * RETURNS
  473. *   
  474. * AUTHOR
  475. *
  476. *   POV-Ray Team
  477. *   
  478. * DESCRIPTION
  479. *
  480. * CHANGES
  481. *
  482. ******************************************************************************/
  483.  
  484. void Destroy_Tnormal(Tnormal)
  485. TNORMAL *Tnormal;
  486. {
  487.   if (Tnormal != NULL)
  488.   {
  489.     Destroy_TPat_Fields ((TPATTERN *)Tnormal);
  490.  
  491.     POV_FREE(Tnormal);
  492.   }
  493. }
  494.  
  495.  
  496.  
  497. /*****************************************************************************
  498. *
  499. * FUNCTION
  500. *
  501. *   Post_Tnormal
  502. *
  503. * INPUT
  504. *   
  505. * OUTPUT
  506. *   
  507. * RETURNS
  508. *   
  509. * AUTHOR
  510. *
  511. *   POV-Ray Team
  512. *   
  513. * DESCRIPTION
  514. *
  515. * CHANGES
  516. *
  517. ******************************************************************************/
  518.  
  519. void Post_Tnormal (Tnormal)
  520. TNORMAL *Tnormal;
  521. {
  522.   int i;
  523.   BLEND_MAP *Map;
  524.  
  525.   if (Tnormal != NULL)
  526.   {
  527.     if (Tnormal->Flags & POST_DONE)
  528.     {
  529.       return;
  530.     }
  531.  
  532.     if (Tnormal->Type == NO_PATTERN)
  533.     {
  534.       Error("No normal type given.");
  535.     }
  536.  
  537.     Tnormal->Flags |= POST_DONE;
  538.  
  539.     if ((Map = Tnormal->Blend_Map) != NULL)
  540.     {
  541.       for (i = 0; i < Map->Number_Of_Entries; i++)
  542.       {
  543.         switch (Map->Type)
  544.         {
  545.           case PIGMENT_TYPE:
  546.  
  547.             Post_Pigment(Map->Blend_Map_Entries[i].Vals.Pigment);
  548.  
  549.             break;
  550.  
  551.           case NORMAL_TYPE:
  552.  
  553.             Post_Tnormal(Map->Blend_Map_Entries[i].Vals.Tnormal);
  554.  
  555.             break;
  556.  
  557.           case TEXTURE_TYPE:
  558.  
  559.             Post_Textures(Map->Blend_Map_Entries[i].Vals.Texture);
  560.  
  561.             break;
  562.  
  563.           case SLOPE_TYPE:
  564.           case COLOUR_TYPE:
  565.           case PATTERN_TYPE:
  566.  
  567.             break;
  568.  
  569.           default:
  570.  
  571.             Error("Unknown pattern type in Post_Tnormal.");
  572.         }
  573.       }
  574.     }
  575.   }
  576. }
  577.  
  578.  
  579.  
  580. /*****************************************************************************
  581. *
  582. * FUNCTION
  583. *
  584. *   Perturb_Normal
  585. *
  586. * INPUT
  587. *   
  588. * OUTPUT
  589. *   
  590. * RETURNS
  591. *   
  592. * AUTHOR
  593. *
  594. *   POV-Ray Team
  595. *   
  596. * DESCRIPTION
  597. *
  598. * CHANGES
  599. *
  600. ******************************************************************************/
  601.  
  602. #define DELTA 0.02
  603.  
  604. void Perturb_Normal(Layer_Normal, Tnormal, EPoint)
  605. VECTOR Layer_Normal, EPoint;
  606. TNORMAL *Tnormal;
  607. {
  608.   VECTOR TPoint,P1;
  609.   DBL value1,value2,Amount;
  610.   int i;
  611.   BLEND_MAP *Blend_Map;
  612.   BLEND_MAP_ENTRY *Prev, *Cur;
  613.  
  614.   /* If normal_map present, use it and return */
  615.  
  616.   if ((Blend_Map=Tnormal->Blend_Map) != NULL)
  617.   {
  618.     if ((Blend_Map->Type == NORMAL_TYPE) && (Tnormal->Type != AVERAGE_PATTERN))
  619.     {
  620.       value1 = Evaluate_TPat((TPATTERN *)Tnormal,EPoint);
  621.  
  622.       Search_Blend_Map (value1,Blend_Map,&Prev,&Cur);
  623.       
  624.       Assign_Vector(P1,Layer_Normal);
  625.  
  626.       Warp_EPoint (TPoint, EPoint, (TPATTERN *)Tnormal);
  627.       Perturb_Normal(Layer_Normal,Cur->Vals.Tnormal,TPoint);
  628.  
  629.       if (Prev != Cur)
  630.       {
  631.         Perturb_Normal(P1,Prev->Vals.Tnormal,TPoint);
  632.  
  633.         value2 = (value1-Prev->value)/(Cur->value-Prev->value);
  634.         value1 = 1.0-value2;
  635.  
  636.         VLinComb2(Layer_Normal,value1,P1,value2,Layer_Normal)
  637.       }
  638.  
  639.       VNormalizeEq(Layer_Normal);
  640.  
  641.       return;
  642.     }
  643.   }
  644.   
  645.   /* No normal_map. */
  646.  
  647.   if (Tnormal->Type <= LAST_NORM_ONLY_PATTERN)
  648.   {
  649.     Warp_EPoint (TPoint, EPoint, (TPATTERN *)Tnormal);
  650.     switch (Tnormal->Type)
  651.       {
  652.        case BITMAP_PATTERN: bump_map (TPoint, Tnormal, Layer_Normal); break;
  653.        case BUMPS_PATTERN:  bumps (TPoint, Tnormal, Layer_Normal);    break;
  654.        case BUMPY1_PATTERN: bumpy1 (TPoint, Tnormal, Layer_Normal);   break;
  655.        case BUMPY2_PATTERN: bumpy2 (TPoint, Tnormal, Layer_Normal);   break;
  656.        case BUMPY3_PATTERN: bumpy3 (TPoint, Tnormal, Layer_Normal);   break;
  657.        case DENTS_PATTERN:  dents (TPoint, Tnormal, Layer_Normal);    break;
  658.        case RIPPLES_PATTERN:ripples (TPoint, Tnormal, Layer_Normal);  break;
  659.        case WAVES_PATTERN:  waves (TPoint, Tnormal, Layer_Normal);    break;
  660.        case WRINKLES_PATTERN:wrinkles (TPoint, Tnormal, Layer_Normal);break;
  661.        case QUILTED_PATTERN:quilted (TPoint, Tnormal, Layer_Normal);  break;
  662.        case AVERAGE_PATTERN: Do_Average_Normals (TPoint, Tnormal, Layer_Normal);  break;
  663.        default:
  664.          Error("Normal pattern not yet implemented.");
  665.       }
  666.   }
  667.   else
  668.   {
  669.     Amount=Tnormal->Amount * -5.0; /*fudge factor*/
  670.     
  671. /* Note, even though DELTA and Pyramid_Vect are constants, we may later
  672.    make DELTA a user-defined parameter.  Good optimising compilers
  673.    should merge the constants anyway. */
  674.    
  675.     for(i=0; i<=3; i++)
  676.     {
  677.       VAddScaled(P1,EPoint,DELTA,Pyramid_Vect[i]);
  678.       value1 = Do_Slope_Map(Evaluate_TPat((TPATTERN *)Tnormal,P1),Blend_Map);
  679.       VAddScaledEq(Layer_Normal,value1*Amount,Pyramid_Vect[i]);
  680.     }
  681.  
  682.   }
  683.  
  684.   VNormalizeEq(Layer_Normal);
  685. }
  686.  
  687.  
  688.  
  689. /*****************************************************************************
  690. *
  691. * FUNCTION
  692. *
  693. * INPUT
  694. *
  695. * OUTPUT
  696. *
  697. * RETURNS
  698. *
  699. * AUTHOR
  700. *
  701. * DESCRIPTION
  702. *
  703. * CHANGES
  704. *
  705. ******************************************************************************/
  706.  
  707. static DBL Do_Slope_Map (value,Blend_Map)
  708. DBL value;
  709. BLEND_MAP *Blend_Map;
  710. {
  711.   DBL Result;
  712.   BLEND_MAP_ENTRY *Prev, *Cur;
  713.  
  714.   if (Blend_Map == NULL)
  715.   {
  716.     return(value);
  717.   }
  718.  
  719.   Search_Blend_Map (value,Blend_Map,&Prev,&Cur);
  720.  
  721.   if (Prev == Cur)
  722.   {
  723.      return(Cur->Vals.Point_Slope[0]);
  724.   }
  725.  
  726.   Result = (value-Prev->value)/(Cur->value-Prev->value);
  727.  
  728.   return(Hermite_Cubic(Result,Prev->Vals.Point_Slope,Cur->Vals.Point_Slope));
  729. }
  730.  
  731.  
  732.  
  733. /*****************************************************************************
  734. *
  735. * FUNCTION
  736. *
  737. * INPUT
  738. *
  739. * OUTPUT
  740. *
  741. * RETURNS
  742. *
  743. * AUTHOR
  744. *
  745. * DESCRIPTION
  746. *
  747. * CHANGES
  748. *
  749. ******************************************************************************/
  750.  
  751. #define S1 UV1[1]
  752. #define S2 UV2[1]
  753. #define P1 UV1[0]
  754. #define P2 UV2[0]
  755.  
  756. static DBL Hermite_Cubic(T1,UV1,UV2)
  757. DBL T1;
  758. UV_VECT UV1;
  759. UV_VECT UV2;
  760. {
  761.   DBL TT=T1*T1;
  762.   DBL TTT=TT*T1;
  763.   DBL rv;        /* simplified equation for poor Symantec */
  764.  
  765.   rv  = TTT*(S1+S2+2.0*(P1-P2));
  766.   rv += -TT*(2.0*S1+S2+3.0*(P1-P2));
  767.   rv += T1*S1 +P1;
  768.  
  769.   return (rv);
  770. }
  771.  
  772.  
  773.  
  774. /*****************************************************************************
  775. *
  776. * FUNCTION
  777. *
  778. * INPUT
  779. *
  780. * OUTPUT
  781. *
  782. * RETURNS
  783. *
  784. * AUTHOR
  785. *
  786. * DESCRIPTION
  787. *
  788. * CHANGES
  789. *
  790. ******************************************************************************/
  791.  
  792. static void Do_Average_Normals (EPoint,Tnormal,normal)
  793. VECTOR EPoint;
  794. TNORMAL *Tnormal;
  795. VECTOR normal;
  796. {
  797.    int i;
  798.    BLEND_MAP *Map = Tnormal->Blend_Map;
  799.    SNGL Value;
  800.    SNGL Total = 0.0;
  801.    VECTOR V1,V2;
  802.    
  803.    Make_Vector (V1, 0.0, 0.0, 0.0);
  804.  
  805.    for (i = 0; i < Map->Number_Of_Entries; i++)
  806.    {
  807.      Value = Map->Blend_Map_Entries[i].value;
  808.      
  809.      Assign_Vector(V2,normal);
  810.  
  811.      Perturb_Normal(V2,Map->Blend_Map_Entries[i].Vals.Tnormal,EPoint);
  812.      
  813.      VAddScaledEq(V1,Value,V2);
  814.  
  815.      Total += Value;
  816.    }
  817.  
  818.    VInverseScale(normal,V1,Total);
  819. }
  820.